/*  Copyright © 2009 Blake Hegerle -*- C++ -*- */

/*  This file is part of metal.
 
    Metal is free software: you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see
    <http://www.gnu.org/licenses/>.

*/

#pragma once

#include <typeinfo>
#include <sstream>
#include <cstring>

#include "forward.h"

namespace metal {
  unsigned int id_sequence();
  
  template<typename T>
  unsigned int id() {
    static unsigned int n = id_sequence();
    return n;
  }

  template<typename T>
  std::string init_name_of() {
    std::stringstream s;
    
    s << "_" << id<T>();

    return s.str();
  }

  template<typename T>
  std::string &name_of() {
    static std::string n(init_name_of<T>());
    return n;
  }

  template<class... Args>
  struct count;

  template<class A, class... Args>
  struct count<A, Args...> {
    static const std::size_t value = 1 + count<Args...>::value;
  };

  template<>
  struct count<> {
    static const std::size_t value = 0;
  };

  template<typename P>
  struct deref_cmp {
    bool operator()(P p1, P p2) { return *p1 == *p2; }
  };

  template<typename T>
  void vector_pack(std::vector<T> &v) {}

  template<typename T, typename Arg, typename... Args>
  void vector_pack(std::vector<T> &v, Arg arg, Args... args) {
    v.push_back(arg);
    vector_pack(v, args...);
  }

  template<typename Arg, typename... Args>
  std::vector<Arg> vector_pack(Arg arg, Args... args) {
    std::vector<Arg> v(count<Args...>::value + 1);

    v.push_back(arg);
    vector_pack(v, args...);

    return v;
  }

  template<typename T, typename... Args>
  std::shared_ptr<T> new_ptr(Args... args) {
    return std::shared_ptr<T>(new T(args...));
  }

  template<typename... Args>
  struct list { };
  
  template<typename L>
  struct is_list {
    static const bool value = false;
  };
  
  template<typename... Args>
  struct is_list<list<Args...> > {
    static const bool value = true;
  };

  template<typename T>
  struct length;

  template<typename... Args>
  struct length<list<Args...> > {
    static const std::size_t value = count<Args...>::value;
  };

  template<typename H, typename T>
  struct cons_list;

  template<typename H, typename... Tail>
  struct cons_list<H, list<Tail...> > {
    typedef list<H, Tail...> type;
  };

  template<typename L>
  struct head {
    typedef void type;
    static_assert(std::is_same<L, L>::value,
		  "cannot take the head of a non-list");
  };

  template<typename H, typename... T>
  struct head<list<H, T...> > {
    typedef H type;
  };

  template<template<typename> class F, typename L>
  struct map_list;
  
  template<template<typename> class F, typename Arg, typename... Args>
  struct map_list<F, list<Arg, Args...> > {
    typedef typename map_list<F, list<Args...> >::type tail;
    typedef typename F<Arg>::type head;
    typedef typename cons_list<head, tail>::type type;
  };
  
  template<template<typename> class F>
  struct map_list<F, list<> > {
    typedef list<> type;
  };
}
